#include "Nano100Series.h"
#include <string.h>
#include "Common.h"
#include "Config.h"

#define DATA_FLASH_BASE        				0xF000
#define DATA_FLASH_CONFIG_NORMAL_BASE		(DATA_FLASH_BASE + FMC_FLASH_PAGE_SIZE * 1)
#define DATA_FLASH_CONFIG_BACKUP_BASE		(DATA_FLASH_BASE + FMC_FLASH_PAGE_SIZE * 2)

struct hg_config tHgConfig;

struct hg_config tHgConfigDefault = {
	2,					// version
	
	// version 1
	{102, 00, 00, 00},	// brightness
	{50, 50, 50, 50},	// contrast
	{30, 50, 50, 50},	// saturation

	// verison 2
	0,					// brightness offset

	0					// reserved
};

uint32_t HgConfigCalcSum(uint32_t *addr, uint32_t count)
{
	uint32_t sum = 0;
	
	while (count--) {
		sum += *(addr++);
	}
	return sum;
}

void HgConfigSetBase(uint32_t base)
{
    uint32_t   au32Config[2];
	uint32_t df_enable_mask = 1;
	uint32_t need_update = 0;

	SYS_UnlockReg();
	FMC_Open();
	
	
    if (FMC_ReadConfig(au32Config, 2) < 0) {
		return;
    }
	
	info("FMC user config[0] : 0x%08X\n", au32Config[0]);
	info("FMC user config[1] : 0x%08X\n", au32Config[1]);
	
	if (au32Config[0] & df_enable_mask) {
		info("data flash is disabled, now enable it\n");
		au32Config[0] &= (~df_enable_mask);
		need_update = 1;
	} else {
		info("data flash is already enabled\n");
	}
	
	if (au32Config[1] == base) {
		info("DFBA(0x%08X) is same as base(0x%08X)\n", au32Config[1], base);
	} else {
		info("DFBA(0x%08X) is different from base(0x%08X), now set it\n", au32Config[1], base);
		au32Config[1] = base;
		need_update = 1;
	}
	
	if (need_update) {
		FMC_ENABLE_CFG_UPDATE();
		if (FMC_WriteConfig(au32Config, 2) < 0) {
			info("write user config failed\n ");
		} else {
			info("write user config ok\n ");
			SYS->IPRST_CTL1 = SYS_IPRST_CTL1_CHIP_RST_Msk;
		}
		FMC_DISABLE_CFG_UPDATE();
	}
}

void HgConfigReadRaw(uint32_t base, struct hg_config *tHgConfig, uint32_t *sum)
{
	int count;
	uint32_t *buf;

	buf = (uint32_t *)tHgConfig;
	SYS_UnlockReg();
	FMC_Open();
	for (count = 0; count < (sizeof(struct hg_config) / 4); count++) {
		*(buf++) = FMC_Read(base + count * 4);
	}
	*sum = FMC_Read(base + sizeof(struct hg_config));
	FMC_Close();
	SYS_LockReg();
}

void HgConfigWriteRaw(uint32_t base, struct hg_config *tHgConfig, uint32_t sum)
{
	int count;
	uint32_t *buf;

	buf = (uint32_t *)tHgConfig;
	SYS_UnlockReg();
	FMC_Open();
	FMC_Erase(base);
	for (count = 0; count < (sizeof(struct hg_config) / 4); count++) {
		FMC_Write(base + count * 4, *(buf++));
	}
	FMC_Write(base + sizeof(struct hg_config), sum);
	FMC_Close();
	SYS_LockReg();
}

void HgConfigRead(struct hg_config *tHgConfig)
{
	uint32_t NormalSumRead;
	uint32_t NormalSumCalc;
	uint32_t BackupSumRead;
	uint32_t BackupSumCalc;
	uint32_t DefaultSumCalc;
	struct hg_config tHgConfigNormal;
	struct hg_config tHgConfigBackup;

	// read normal config
	HgConfigReadRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &tHgConfigNormal, &NormalSumRead);
	NormalSumCalc = HgConfigCalcSum((uint32_t *)&tHgConfigNormal, sizeof(struct hg_config) / 4);
	
	// read backup config
	HgConfigReadRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &tHgConfigBackup, &BackupSumRead);
	BackupSumCalc = HgConfigCalcSum((uint32_t *)&tHgConfigBackup, sizeof(struct hg_config) / 4);
	
	if (NormalSumCalc == NormalSumRead) {
		// normal config is ok, 
		if (BackupSumCalc == BackupSumRead) {
			// backup config is ok
			// return
			memcpy(tHgConfig, &tHgConfigNormal, sizeof(struct hg_config));
			return;
		} else {
			// backup config is wrong, save backup config from normal config
			HgConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &tHgConfigNormal, NormalSumCalc);
			// return
			memcpy(tHgConfig, &tHgConfigNormal, sizeof(struct hg_config));
			return;
		}
		
	} else {
		// normal config is wrong, try backup config
		if (BackupSumCalc == BackupSumRead) {
			// backup config is ok, save normal backup from backup config
			HgConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &tHgConfigBackup, BackupSumCalc);
			// return
			memcpy(tHgConfig, &tHgConfigBackup, sizeof(struct hg_config));
			return;
		} else {
			// backup config is wrong, save normal config and backup config from default config
			DefaultSumCalc = HgConfigCalcSum((uint32_t *)&tHgConfigDefault, sizeof(struct hg_config) / 4);
			HgConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &tHgConfigDefault, DefaultSumCalc);
			HgConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &tHgConfigDefault, DefaultSumCalc);
			memcpy(tHgConfig, &tHgConfigDefault, sizeof(struct hg_config));
			return;
		}	
	}
}

void HgConfigWrite(struct hg_config *tHgConfig)
{
	uint32_t sum;
	
	sum = HgConfigCalcSum((uint32_t *)tHgConfig, sizeof(struct hg_config) / 4);
	HgConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, tHgConfig, sum);
	HgConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, tHgConfig, sum);
}

void HgConfigDump(void)
{
	info("Dump tHgConfig begin\n");
	info("tHgConfig.version : %u\n", tHgConfig.version);
	info("tHgConfig.tBrightness.w : %u\n", tHgConfig.tBrightness.w);
	info("tHgConfig.tBrightness.r : %u\n", tHgConfig.tBrightness.r);
	info("tHgConfig.tBrightness.g : %u\n", tHgConfig.tBrightness.g);
	info("tHgConfig.tBrightness.b : %u\n", tHgConfig.tBrightness.b);
	info("tHgConfig.tContrast.w : %u\n", tHgConfig.tContrast.w);
	info("tHgConfig.tContrast.r : %u\n", tHgConfig.tContrast.r);
	info("tHgConfig.tContrast.g : %u\n", tHgConfig.tContrast.g);
	info("tHgConfig.tContrast.b : %u\n", tHgConfig.tContrast.b);
	info("tHgConfig.tSaturation.w: %u\n", tHgConfig.tSaturation.w);
	info("tHgConfig.tSaturation.r: %u\n", tHgConfig.tSaturation.r);
	info("tHgConfig.tSaturation.g: %u\n", tHgConfig.tSaturation.g);
	info("tHgConfig.tSaturation.b: %u\n", tHgConfig.tSaturation.b);
	info("Dump tHgConfig end\n");
}

void HgConfigInit(void)
{
	HgConfigSetBase(DATA_FLASH_BASE);
	HgConfigRead(&tHgConfig);
	info("Read config :\n");
	HgConfigDump();
}

